const { src, dest, parallel, series, watch } = require('gulp')

const del = require('del')

const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()

const browserSync = require('browser-sync')
const bs = browserSync.create()
const cwd = process.cwd()

// page 读取项目中的个性配置文件
let config = {
    // defalut config
    build: {
        src: 'src',
        dist: 'dist',
        temp: 'temp',
        public: 'public',
        paths: {
            styles: 'assets/styles/*.scss',
            scripts: 'assets/scripts/*.js',
            pages: '*.html',
            images: 'assets/images/**',
            fonts: 'assets/fonts/**'
        }
    }
}

try {
    const loadConfig = require(`${cwd}/vivijen-gulp-pages.config.js`)
    config = Object.assign({}, config, loadConfig)
} catch (e) {}

// 已编译过后为防止再次编译时文件已存在，清空 dist 目录及目录下文件
const clean = () => {
    return del([config.build.dist, config.build.temp]) //把编译结果放入到 temp 目录下，最终 useref 执行后的结果才放 dist 目录
}

// 编译 scss 文件，使之变成浏览器支持的 css 文件
const style = () => {
    return src(config.build.paths.styles, { base: config.build.src, cwd: config.build.src })
     .pipe(plugins.sass({ outputStyle: 'expanded' }))
     .pipe(dest(config.build.temp))
}

// 编译js代码里的 ECMAScript 新特性为绝大部分浏览器支持的老写法 
const script = () => {
    return src(config.build.paths.scripts, { base: config.build.src, cwd: config.build.src })
     .pipe(plugins.babel({ presets:[require('@babel/preset-env')] }))
     .pipe(dest(config.build.temp))
}

// html 模板编译成正常的 html 文件
const page = () => {
    return src(config.build.paths.pages, { base: config.build.src, cwd: config.build.src }) //'src/**/*.html' src 任意目录下的 html 文件
     .pipe(plugins.swig({ data: config.data })) // 可以简写成 .pipe(swig({ data }))
     .pipe(dest(config.build.temp))
     .pipe(bs.reload({ stream: true })) // 有些项目中监听 css\js\html 构建的热更新不适用 watch 不支持，也可以理解，因为watch 的方式不支持在参数后面添加其他任务参数，所以可以在构建任务定中用 bs.reload 替代, 任务都用 bs.reload 来将流推入浏览器时，bs.init() 里面的 files 配置就不需要了
}

// 图片文件压缩，无损压缩
const image = () => {
    return src(config.build.paths.images, { base: config.build.src, cwd: config.build.src })
     .pipe(plugins.imagemin())
     .pipe(dest(config.build.dist))
     .pipe(bs.reload({ stream: true }))
}

// 字体文件压缩
const font = () => {
    return src(config.build.paths.fonts, { base: config.build.src, cwd: config.build.src })
     .pipe(plugins.imagemin())
     .pipe(dest(config.build.dist))
     .pipe(bs.reload({ stream: true }))
}

// 其他一些不需要特殊处理的文件也需要平移到 dist 目录
const extra = () => {
    return src('**', { base: config.build.public, cwd: config.build.public })
     .pipe(dest(config.build.dist))
}

// 开发过程中的热更新
const serve = () => {
    watch(config.build.paths.styles, { cwd: config.build.src }, style) // 有些项目中监听 css\js\html 构建的热更新不适用 watch 不支持，也可以理解，因为watch 的方式不支持在参数后面添加其他任务参数，所以可以在构建任务定中用 bs.reload 替代
    watch(config.build.paths.scripts, { cwd: config.build.src }, script)
    watch(config.build.paths.pages, { cwd: config.build.src }, page)
    // watch('src/assets/images/**', image)
    // watch('src/assets/fonts/**', font)
    // watch('public/**', extra)

    // 不是说不构建，而是说尝试减少 watch 的次数
    watch([
        config.build.paths.images,
        config.build.paths.fonts
    ], { cwd: config.build.src }, bs.reload) // 这些路径下的文件变化时，浏览器会刷新重新获取文件
    
    watch('**', { cwd: config.build.public }, bs.reload)

    // 执行 serve 再去修改 src 目录下的文件，则修改会直接在浏览器中实现热更新
    // 可能会因为swig 模板引擎缓存的机制导致页面不会发生变化，此时需要额外将 swig 选项中的 cache 设置为 false

    bs.init({
        notify: false, // browser 页面提示关闭
        port: 2080, // 设置 browser 启动的端口
        // open: false, // 取消 browser 启动时自动打开浏览器
        // files: 'dist/**', // 被 browser 启动监听的路径通配符
        serve: {
            baseDir:  [config.build.temp, config.build.src, config.build.public], // 请求过来后会从这些目录依次去找文件
            routes: {
                '/node_modules' : 'node_modules'
            }
        }
    })
}

// useref 处理开发和打包后，文件路径变化而代码中的引用路径需要同时改变问题
const useref = () => {
    return src(config.build.paths.pages, { base: config.build.temp, cwd: config.build.temp })
     .pipe(plugins.useref({ searchPath: [config.build.temp, '.'] }))
     .pipe(plugins.if(/\.js$/, plugins.uglify()))
     .pipe(plugins.if(/\.css$/, plugins.cleanCss()))
     .pipe(plugins.if(/\.html$/, plugins.htmlmin({
         collapseWhitespace: true,
         minifyCSS: true,
         minifyJS: true
     })))
     .pipe(dest(config.build.dist))
}

const compile = parallel(style, script, page)

// 上线之前执行的任务
const build = series(clean, parallel(series(compile, useref), image, font, extra))

const develop = series(compile, serve)

module.exports = {
    clean, // 清除 编译/打包 缓存
    build, // 打包命令
    develop // 开发阶段启动的任务
}